package drds.global_transaction.global_transaction_manager;

import drds.common.Log;
import drds.global_transaction.api.api.GlobalTransactionStatus;
import drds.global_transaction.api.exception.GlobalTransactionException;
import drds.global_transaction.api.exception.TransactionExceptionCode;
import drds.global_transaction.api.message_codec.request_and_response.transaction.TransactionRequest;
import drds.global_transaction.api.message_codec.request_and_response.transaction.TransactionResponse;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_begin.GlobalTransactionBeginRequestMessage;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_begin.GlobalTransactionBeginResponseMessage;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_end_status_report.GlobalTransactionStatusReportRequestMessage;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_end_status_report.GlobalTransactionStatusReportResponseMessage;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_end_status_report.global_transaction_commit_status_report.GlobalTransactionCommitRequestMessage;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_end_status_report.global_transaction_commit_status_report.GlobalTransactionCommitResponseMessage;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_end_status_report.global_transaction_rollback_status_report.GlobalTransactionRollbackRequestMessage;
import drds.global_transaction.api.message_codec.request_and_response.transaction.global_transaction.global_transaction_end_status_report.global_transaction_rollback_status_report.GlobalTransactionRollbackResponseMessage;
import drds.global_transaction.api.rpc.rpc_client.transaction_manager.TransactionManagerRpcClient;
import drds.global_transaction.common.GlobalTransactionIds;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeoutException;

@Slf4j
public class GlobalTransactionManager implements drds.global_transaction.api.api.GlobalTransactionManager
{

	private static class SingletonHolder
	{
		private static final drds.global_transaction.api.api.GlobalTransactionManager INSTANCE = new GlobalTransactionManager();
	}

	public static drds.global_transaction.api.api.GlobalTransactionManager get()
	{
		return SingletonHolder.INSTANCE;
	}

	private GlobalTransactionManager()
	{

	}

	@Override
	public GlobalTransactionStatus getGlobalTransactionStatus(String globalTransactionIdWithAddressAndPort) throws GlobalTransactionException
	{
		long globalTransactionId = GlobalTransactionIds.getGlobalTransactionId(globalTransactionIdWithAddressAndPort);
		GlobalTransactionStatusReportRequestMessage globalTransactionStatusRequestMessage = new GlobalTransactionStatusReportRequestMessage();
		globalTransactionStatusRequestMessage.setGlobalTransactionId(globalTransactionId);
		GlobalTransactionStatusReportResponseMessage globalTransactionStatusResponseMessage = (GlobalTransactionStatusReportResponseMessage) sendMessageWithResponse(globalTransactionStatusRequestMessage);
		return globalTransactionStatusResponseMessage.getGlobalTransactionStatus();
	}

	@Override
	public String globalTransactionBegin(String applicationId, String transactionServiceGroup, String transactionName, int globalTransactionTimeout) throws GlobalTransactionException
	{
		GlobalTransactionBeginRequestMessage globalTransactionBeginRequestMessage = new GlobalTransactionBeginRequestMessage();
		globalTransactionBeginRequestMessage.setTransactionName(transactionName);
		globalTransactionBeginRequestMessage.setGlobalTransactionTimeout(globalTransactionTimeout);
		GlobalTransactionBeginResponseMessage globalTransactionBeginResponseMessage = (GlobalTransactionBeginResponseMessage) sendMessageWithResponse(globalTransactionBeginRequestMessage);
		return globalTransactionBeginResponseMessage.getGlobalTransactionIdWithAddressAndPort();
	}

	@Override
	public GlobalTransactionStatus globalTransactionCommit(String globalTransactionIdWithAddressAndPort) throws GlobalTransactionException
	{
		long globalTransactionId = GlobalTransactionIds.getGlobalTransactionId(globalTransactionIdWithAddressAndPort);
		GlobalTransactionCommitRequestMessage globalTransactionCommitRequestMessage = new GlobalTransactionCommitRequestMessage();
		globalTransactionCommitRequestMessage.setGlobalTransactionId(globalTransactionId);
		GlobalTransactionCommitResponseMessage globalTransactionCommitResponseMessage = (GlobalTransactionCommitResponseMessage) sendMessageWithResponse(globalTransactionCommitRequestMessage);
		return globalTransactionCommitResponseMessage.getGlobalTransactionStatus();
	}

	@Override
	public GlobalTransactionStatus globalTransactionRollback(String globalTransactionIdWithAddressAndPort) throws GlobalTransactionException
	{
		long globalTransactionId = GlobalTransactionIds.getGlobalTransactionId(globalTransactionIdWithAddressAndPort);
		GlobalTransactionRollbackRequestMessage globalTransactionRollbackRequestMessage = new GlobalTransactionRollbackRequestMessage();
		globalTransactionRollbackRequestMessage.setGlobalTransactionId(globalTransactionId);
		GlobalTransactionRollbackResponseMessage globalTransactionRollbackResponseMessage = (GlobalTransactionRollbackResponseMessage) sendMessageWithResponse(globalTransactionRollbackRequestMessage);
		return globalTransactionRollbackResponseMessage.getGlobalTransactionStatus();
	}

	private TransactionResponse sendMessageWithResponse(TransactionRequest transactionRequest) throws GlobalTransactionException
	{
		try
		{
			return (TransactionResponse) TransactionManagerRpcClient.getInstance().asynchronousSendRequestWithTimeoutGetResponse(transactionRequest);
		} catch (TimeoutException e)
		{
			Log.error(log, Thread.currentThread(), TimeoutException.class.getSimpleName(), e);
			throw new GlobalTransactionException(TransactionExceptionCode.io, e);
		}
	}
}
